package com.dylan.历史.多线程.并发编程的艺术.编程基础;

import com.dylan.历史.util.SleepUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;

/**
 * Created by Dylan on 2018/7/15.
 *
 * 等待通知的范式
 *
 * 等待方遵循如下原则:
 * 1.获取对象的锁
 * 2.如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件
 * 3.条件满足则执行对应的逻辑
 * synchronized (对象) {
 *     while(条件不满足) {
 *         对象.wait();
 *     }
 *     对应的处理逻辑
 * }
 *
 * 通知方遵循如下原则:
 * 1.获得对象的锁
 * 2.改变条件
 * 3.通知所有等待在对象上的线程
 * synchronized (对象) {
 *     改变条件
 *     对象.notifyAll();
 * }
 */
public class 等待通知机制 {

    public static void main(String[] args) {
        Thread waitThread = new Thread(new Wait(), "WaitThread");
        waitThread.start();
        SleepUtils.seconds(1);
        Thread notifyThread = new Thread(new Notify(), "NotifyThread");
        notifyThread.start();
    }

    private static final Object lock = new Object();
    private static volatile boolean flag = true;

    static class Wait implements Runnable {

        @Override
        public void run() {
            // 加锁,拥有lock的Monitor
            synchronized (lock) {
                // 当条件不满足时,继续wait,同时释放了lock的锁
                while (flag) {
                    try {
                        System.out.println(Thread.currentThread() + " flag is true. wait" +
                                "@" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 条件满足时,完成工作
                System.out.println(Thread.currentThread() + " flag is false. wait" +
                        "@" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            }
        }
    }

    static class Notify implements Runnable {

        @Override
        public void run() {
            // 加锁,拥有lock的Monitor
            synchronized (lock) {
                // 获取lock的锁,然后进行通知,通知时不会释放lock锁.
                // 直到当前线程释放了lock后,WaitThread才能从wait()方法返回
                System.out.println(Thread.currentThread() + " flag is false. wait" +
                        "@" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
                lock.notifyAll();
                flag = false;
                SleepUtils.seconds(2);
            }

            // 再次加锁
            synchronized (lock) {
                System.out.println(Thread.currentThread() + " flag is false. wait" +
                        "@" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
                SleepUtils.seconds(2);
            }
        }
    }
}
